# coding: utf-8
import json
import os
import time

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException

from src.geo import get_location

PROJECT_DIR = os.path.dirname(os.path.dirname(__file__))


def main(province, city, provider='全部'):
    spider = DeliveryNetworkSpider(
        driver_path=os.path.join(PROJECT_DIR, 'dep/mac/chromedriver'))

    try:
        results = spider.search(province=province, city=city, provider=provider)
    except Exception as err:
        print(err)
    else:
        for result in results:
            print(result)
            store_result(os.path.join(PROJECT_DIR, 'results', f'{province}-{city}-{provider}.jl'), result)

    finally:
        spider.close()


class XPathExpression:
    # 省份下拉框 Tab
    PROVINCE_TAB = '//*[@id="provinces"]/div/ul/li[2]'
    # 具体某个省份
    PROVINCE_ITEM = '//*[contains(@class, "place province") and contains(text(), "{}")]'
    # 具体某个城市
    CITY_ITEM = '//*[contains(@class, "place city") and contains(text(), "{}")]'
    # 具体某个区县
    COUNTY_ITEM = '//*[contains(@class, "place county") and contains(text(), "{}")]'
    # 右侧查询按钮
    QUERY_BUTTON = '//*[contains(@class, "btn-query")]'
    # 快递公司选择
    PROVIDER_ITEM = '//ul[@id="companyCount"]/li/*[contains(text(), "{}")]'
    # 下一页
    NEXT_PAGE_BUTTON = '//*[contains(@class, "page-down-active")]'
    # 页面中的快递公司信息条目
    QUERY_ITEMS = '//*[@id="queryResult"]/dl'


class DeliveryNetworkSpider(object):
    """基于快递 100 的快递网点查询爬虫
    """

    def __init__(self, driver_path):
        """
        初始化快递网点爬虫，关键参数是要指定 webdriver 路径，否则
        无法控制浏览器进行模拟查询。当前仅支持 Chrome 浏览器。

        :param driver_path: 默认是 Mac 版本所在的路径，如果是 win
                版本，需要自行修改路径。
        """
        # Windows 下需要替换成对应的 chromedriver，可以在 dep 目录下新建 win
        # 目录，将 windows 版本的放到目录下，并修改 mac->win
        self._driver = webdriver.Chrome(executable_path=driver_path)
        self._driver.implicitly_wait(10)  # 隐式等待元素出现

    def close(self):
        try:
            self._driver.close()
        except:
            pass

    def search(self, province, city, county='暂不选择', provider='全部'):
        """执行网点查询的核心方法。

        :param province: 省份（一定要是快递 100 上有的名字）
        :param city: 城市（一定要是快递 100 上有的名字）
        :param county: 区县，默认为全部区县
        :param provider: 快递服务商名字，不传则查询所有快递公司
        """
        self._open_home_page()
        self._select_location(province, city, county)
        self._select_provider(provider)
        while True:
            yield from self._parse_page(province, city)
            if self._has_next_page():
                time.sleep(.5)  # 防止翻页太快被抓到
                self._visit_next_page()
            else:
                break

    def _open_home_page(self):
        self._driver.get('https://www.kuaidi100.com/network/')

    def _select_location(self, province, city, county):
        # 找到输入下拉框位置
        elem = self._driver.find_element_by_id('provinceSelect')
        self.__highlight(elem)
        elem.click()

        # 定位选择省份 Tab
        elem = self._driver.find_element_by_xpath(XPathExpression.PROVINCE_TAB)
        self.__highlight(elem)
        elem.click()

        # 接下来选择省份
        elem = self._driver.find_element_by_xpath(
            XPathExpression.PROVINCE_ITEM.format(province))
        self.__highlight(elem)
        elem.click()

        # 紧接着选择城市
        elem = self._driver.find_element_by_xpath(
            XPathExpression.CITY_ITEM.format(city))
        self.__highlight(elem)
        elem.click()

        # 选择所有区域
        elem = self._driver.find_element_by_xpath(
            XPathExpression.COUNTY_ITEM.format(county))
        self.__highlight(elem)
        elem.click()

        # 定位到查询按钮，并点击查询跳转页面
        elem = self._driver.find_element_by_xpath(XPathExpression.QUERY_BUTTON)
        self.__highlight(elem)
        elem.click()

    def _select_provider(self, provider):
        elem = self._driver.find_element_by_xpath(
            XPathExpression.PROVIDER_ITEM.format(provider))
        self.__highlight(elem)
        elem.click()

    def _has_next_page(self):
        try:
            self._driver.find_element_by_xpath(XPathExpression.NEXT_PAGE_BUTTON)
        except NoSuchElementException:
            return False
        else:
            return True

    def _visit_next_page(self):
        elem = self._driver.find_element_by_xpath(XPathExpression.NEXT_PAGE_BUTTON)
        self.__highlight(elem)
        elem.click()

    def __highlight(self, elem):
        """高亮操作的元素"""
        self._driver.execute_script(
            "arguments[0].setAttribute('style',arguments[1]);",
            elem,
            "outline:2px solid red;")
        time.sleep(.2)

    def _parse_page(self, province, city):
        try:
            elements = self._driver.find_elements_by_xpath(XPathExpression.QUERY_ITEMS)
        except NoSuchElementException:
            return []
        else:
            for el in elements:
                yield self._extract(el.text, province, city)

    @staticmethod
    def _extract(text, province, city):
        item = dict(
            province=province,
            city=city,
            name='',  # 名称
            address='',  # 地址
            contact_phone='',  # 联系电话
            pickup_phone='',  # 取件电话
            check_phone='',  # 查件电话
            complaint_phone='',  # 投诉电话
            location=dict(
                lng=0.0,  # 经度
                lat=0.0,  # 纬度
            )
        )
        if not text:
            return item

        # 提取出纯文本后换行解析
        lines = [l.strip() for l in text.splitlines() if l.strip()]

        def __(k):
            r = [l for l in lines if l.startswith(k)]
            return r[0].replace(k, '') if r else ''

        item['name'] = lines[0]
        item['address'] = __('公司地址：')
        item['contact_phone'] = __('联系电话：')
        item['pickup_phone'] = __('取件电话：')
        item['check_phone'] = __('查件电话：')
        item['complaint_phone'] = __('投诉电话：')
        item['location'] = get_location(
            item['address'], city=u"{}{}".format(province, city))

        return item


def store_result(to_path, result):
    d = os.path.dirname(to_path)
    os.makedirs(d, exist_ok=True)
    with open(to_path, 'a+') as f:
        f.write('{}\n'.format(json.dumps(result, ensure_ascii=False)))


if __name__ == '__main__':
    # 这里修改需要抓取的目标地点和目标公司
    # 每次都会将抓到结果存入 results 目录下，且如果文件存在，
    # 则每次都是追加。害怕重复的话，可以每次跑的时候手动删除文件。
    # 存储的文件格式：每一行都是一个 json 串，可以使用 `json.loads`
    # 加载行成为 Python 字典。
    main(province='北京', city='北京', provider='中通')
